library(tidyverse)
library(langcog)
library(psych)
library(readxl)
library(cowplot)
library(lme4)
library(lmerTest)
library(kableExtra)
library(lubridate)
library(ggdendro)
theme_set(theme_bw())
heatmap_fun <- function(efa, factor_names = NA){
# get factor names
if(is.na(factor_names)){
factor_names <- paste("Factor", 1:efa$factors)
}
# put factors in a standard order when applicable
body_factors <- factor_names[grepl("BODY", factor_names)]
leftovers <- factor_names[!factor_names %in% body_factors]
heart_factors <- leftovers[grepl("HEART", leftovers)]
leftovers <- leftovers[!leftovers %in% heart_factors]
mind_factors <- leftovers[grepl("MIND", leftovers)]
other_factors <- leftovers[!leftovers %in% mind_factors]
factor_levels <- c(body_factors, heart_factors, mind_factors, other_factors)
# get factor loadings
loadings <- efa$loadings[] %>%
data.frame() %>%
rownames_to_column("capacity") %>%
gather(factor, loading, -capacity) %>%
mutate(factor = as.character(factor(factor, labels = factor_names)),
factor = factor(factor, levels = factor_levels))
# get fa.sort() order
order <- loadings %>%
group_by(capacity) %>%
top_n(1, abs(loading)) %>%
ungroup() %>%
arrange(desc(factor), abs(loading)) %>%
mutate(order = 1:length(levels(factor(loadings$capacity)))) %>%
select(capacity, order)
# get percent shared variance explained
shared_var <- efa$Vaccounted %>%
data.frame() %>%
rownames_to_column("stat") %>%
filter(stat == "Proportion Explained") %>%
select(-stat) %>%
gather(factor, var) %>%
mutate(factor = as.character(factor(factor, labels = factor_names)),
factor = factor(factor, levels = factor_levels)) %>%
mutate(var_shared = paste0(factor, "\n", round(var, 2)*100, "% shared var.,"))
# get percent total variance explained
total_var <- efa$Vaccounted %>%
data.frame() %>%
rownames_to_column("stat") %>%
filter(stat == "Proportion Var") %>%
select(-stat) %>%
gather(factor, var) %>%
mutate(factor = as.character(factor(factor, labels = factor_names)),
factor = factor(factor, levels = factor_levels)) %>%
mutate(var_total = paste0(round(var, 2)*100, "% total var."))
# make plot
plot <- ggplot(loadings %>%
left_join(order) %>%
left_join(shared_var %>% select(-var)) %>%
left_join(total_var %>% select(-var)) %>%
mutate(capacity = gsub("_", " ", capacity),
factor = factor(factor, levels = factor_levels),
xlab = paste(var_shared, var_total, sep = "\n")),
aes(x = reorder(xlab, as.numeric(factor)),
y = reorder(capacity, order),
fill = loading,
label = format(round(loading, 2), nsmall = 2))) +
geom_tile(color = "black") +
geom_text(size = 3) +
scale_fill_distiller(limits = c(-1, 1),
palette = "RdYlBu",
guide = guide_colorbar(barheight = 10)) +
theme_minimal() +
scale_x_discrete(position = "top") +
theme(axis.title = element_blank())
return(plot)
}
s_moments <- function(p) {p*(p+1)/2}
param_est <- function(p, k) {p*k + p - (k*(k-1)/2)}
check_ok <- function(p, k) {
a <- (p-k)^2
b <- p+k
return(ifelse(a>b, TRUE, FALSE))
}
max_ok <- function(p) {
df_check <- data.frame()
for(i in 1:p){
df_check[i,"check"] <- check_ok(p,i)
}
max <- df_check %>% filter(check) %>% nrow()
return(max)
}
reten_fun <- function(df, rot_type = c("oblimin", "varimax", "none")){
# figure out max number of factors to retain
n_var <- length(names(df))
max_k <- max_ok(n_var)
# run efa with max factors, unrotated
fa_unrot <- fa(df, nfactors = max_k, rotate = "none",
scores = "tenBerge", impute = "median")
eigen <- fa_unrot$Vaccounted %>%
data.frame() %>%
rownames_to_column("param") %>%
gather(factor, value, -param) %>%
spread(param, value) %>%
filter(`SS loadings` > 1, `Proportion Explained` > 0.05)
retain_k <- nrow(eigen)
fa_rot <- fa(df, nfactors = retain_k, rotate = rot_type,
scores = "tenBerge", impute = "median")
loadings <- fa_rot$loadings[] %>%
data.frame() %>%
rownames_to_column("capacity") %>%
gather(factor, loading, -capacity) %>%
group_by(capacity) %>%
top_n(1, abs(loading)) %>%
ungroup() %>%
count(factor)
retain_k_final <- nrow(loadings)
return(retain_k_final)
}
source("./scripts/p7_data_prep.R")
Joining, by = c("p7_entr", "p7_2day", "p7_ver", "p7_batc", "p7_resample", "p7_ctry", "p7_subj", "p7_file", "p7_recr", "p7_wher", "p7_whoc", "p7_abs_child.exp", "p7_abs_poetic", "p7_abs_tv.real", "p7_abs_see.image", "p7_abs_mind.world", "p7_abs_clouds", "p7_abs_vivid.dreams", "p7_abs_mystic.exp", "p7_abs_step.outside", "p7_abs_textures", "p7_abs_too.real", "p7_abs_music.attn", "p7_abs_heavy.body", "p7_abs_sense.presc", "p7_abs_fire", "p7_abs_nature.art", "p7_abs_colors", "p7_abs_thght.wander", "p7_abs_vivid.past", "p7_abs_makes.sense", "p7_abs_become.chctr", "p7_abs_visual.thghts", "p7_abs_small.things", "p7_abs_music.lift", "p7_abs_noise.music", "p7_abs_scented.mem", "p7_abs_visual.music", "p7_abs_before.said", "p7_abs_physical.mem", "p7_abs_voice.sound", "p7_abs_not.physical", "p7_abs_thgts.image", "p7_abs_odor.to.color", "p7_abs_sunset", "p7_abs_total", "p7_abs_check", "p7_dse_god.prescn", "p7_dse_conect.life", "p7_dse_no.daily.conc", "p7_dse_spi.strength", "p7_dse_spirt.comfort", "p7_dse_inner.peace", "p7_dse_god.help", "p7_dse_guided.daily", "p7_dse_direct.love", "p7_dse_lov.thru.othr", "p7_dse_touch.by.beau", "p7_dse_blessings", "p7_dse_selfless.care", "p7_dse_accept.wrong", "p7_dse_total", "p7_dse_check", "p7_se_voice.out", "p7_se_voice.in", "p7_se_placed.thought", "p7_se_vision.out", "p7_se_image.in", "p7_se_touch", "p7_se_smell", "p7_se_taste", "p7_se_dream.sent", "p7_se_stand.beside", "p7_se_demon.in.room", "p7_se_spnat.presence", "p7_se_shaking.prayer", "p7_se_emotion.prayer", "p7_se_powrful.prayer", "p7_se_out.body.exp", "p7_se_body.control", "p7_se_slep.paralysis", "p7_se_god.thru.pain", "p7_se_god.illness", "p7_se_live.healing", "p7_se_own.healing", "p7_se_total", "p7_se_check", "p7_wob_set.mind_reverse", "p7_wob_find.ways_reverse", "p7_wob_own.hands_reverse", "p7_wob_future.on.me_reverse", "p7_wob_little.change", "p7_wob_helpless", "p7_wob_others.do", "p7_wob_beynd.control", "p7_wob_interfere", "p7_wob_little.contrl", "p7_wob_cant.solve", "p7_wob_pushed.around", "p7_wob_total", "p7_unev_voice.aloud", "p7_unev_phone.ring", "p7_unev_call.name", "p7_unev_music", "p7_unev_no.ones.vox", "p7_unev_shadows", "p7_unev_total", "p7_unev_check", "p7_exsen_esp.exists", "p7_exsen_esp.exp", "p7_exsen_psychic", "p7_exsen_view.future", "p7_exsen_dream.true", "p7_exsen_dist.msg", "p7_exsen_send.msg", "p7_exsen_total", "p7_exsen_check", "p7_hthk_complex", "p7_hthk_responsblt", "p7_hthk_not.fun", "p7_hthk_lil.challeng", "p7_hthk_avoid.think", "p7_hthk_long.hrs", "p7_hthk_hrd.hav.to", "p7_hthk_smal.daily", "p7_hthk_lil.thought", "p7_hthk_way.to.top", "p7_hthk_new.soltions", "p7_hthk_not.exciting", "p7_hthk_puzzles", "p7_hthk_abstract", "p7_hthk_intel.task", "p7_hthk_mental.effrt", "p7_hthk_job.done", "p7_hthk_not.personal", "p7_hthk_total", "p7_por_thgs.hrt", "p7_por_thgs.hurt_a", "p7_por_thgs.hurt_b", "p7_por_thgs.hurt_c", "p7_por_wifi.thgs", "p7_por_job.wish", "p7_por_angr.cntrl", "p7_por_angr.cntrl_a", "p7_por_angr.cntrl_b", "p7_por_angr.cntrl_c", "p7_por_sprt.envy", "p7_por_read.thgs", "p7_por_read.thgs_a", "p7_por_read.thgs_b", "p7_por_read.thgs_c", "p7_por_stre.spoil", "p7_por_stre.spoil_a", "p7_por_stre.spoil_b", "p7_por_stre.spoil_c", "p7_por_conslt.unseen", "p7_por_mircl.prayer", "p7_por_pry.dead.back", "p7_por_spkn.curse", "p7_por_spkn.curse_a", "p7_por_spkn.curse_b", "p7_por_spkn.curse_c", "p7_por_curse.sick", "p7_por_sprt.put.thgs", "p7_por_fall.in.lov", "p7_por_fall.in.lov_a", "p7_por_fall.in.lov_b", "p7_por_fall.in.lov_c", "p7_por_thgs.heal", "p7_por_visualization", "p7_por_total", "p7_por_check", "p7_mm_ang_feel.hurt", "p7_mm_ang_thgs.hurt", "p7_mm_ang_sprt.hurt", "p7_mm_ang_physical", "p7_mm_ang_sickness", "p7_mm_car_fel.no.pr", "p7_mm_car_thk.no.pr", "p7_mm_car_sprt.help", "p7_mm_car_physical", "p7_mm_car_curing", "p7_mm_env_feel.hurt", "p7_mm_env_thgs.hurt", "p7_mm_env_sprt.hurt", "p7_mm_env_physical", "p7_mm_env_sickness", "p7_mm_thnk.feel.hurt", "p7_mm_sprt.thgs.hurt", "p7_mm_total", "p7_mm_check", "p7_dem_sex", "p7_dem_age", "p7_dem_pocc", "p7_dem_major", "p7_dem_ethnicity", "p7_dem_rur.urb", "p7_dem_affrd.basics", "p7_dem_ses", "p7_dem_how.sprt.relg", "p7_dem_religion", "p7_dem_church", "p7_dem_holy.tung.gif")
NAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercion
d1 <- d0 %>%
select(-contains("_dem_"), -ends_with("_cat"), -p7_ctry,
-contains("total"), -contains("check")) %>%
gather(question, response, -p7_subj) %>%
# rescale everything to be in 0, 1
mutate(scale = gsub("p7_", "", question),
scale = gsub("_.*$", "", scale),
response = case_when(
scale %in% c("abs", "exsen") ~ response,
scale == "dse" ~ response / 5,
scale == "hthk" ~ (response + 2) / 4,
scale %in% c("mm", "unev") ~ response / 3,
scale == "por" ~ response / 2,
scale == "se" ~ response / 4,
scale == "wob" ~ (response + 3) / 6)) %>%
# get rid of follow-up questions for porosity
filter(!grepl("_.$", question)) %>%
select(-scale) %>%
spread(question, response) %>%
column_to_rownames("p7_subj")
This is an exploration of dimensionality reduction on Packet 7 (last updated: 2019-05-19). KW thought of this in response to the general question of how (if at all) we can distinguish porosity “beliefs” from spiritual “experiences.”
The basic question here is whether we can distinguish beliefs from experience by tracking patterns of covariance across individual participants: What are the “clusters” of questions that tended to hang together at the participant level (i.e., if someone endorsed one question, what else did they endorse)? If porosity and spiritual experience are fully redundant with each other, we would not expect to see separate clusters of porosity vs. spiritual experience items.
Main take-away: If we retain more than 2-3 factors, we end up distinguishing between porosity and spiritual experience. Two standard methods of determining how many factors to retain (parallel analysis and minimizing BIC) both suggest retaining six or more factors.
Exploratory factor analysis (EFA)
Parallel analysis
fa.parallel(d1)
Parallel analysis suggests that the number of factors = 12 and the number of components = 11

Parallel analysis suggests retaining 13 factors.
efa13 <- fa(d1, 13, rotate = "varimax")
heatmap_fun(efa13, factor_names = paste0("MR", 1:13))
the condition has length > 1 and only the first element will be usedJoining, by = "capacity"
Joining, by = "factor"
Joining, by = "factor"

efa13_loadings <- efa13$loadings[] %>%
data.frame() %>%
rownames_to_column("question") %>%
gather(factor, loading, -question) %>%
mutate(scale = gsub("p7_", "", question),
scale = gsub("_.*$", "", scale),
factor = factor(factor, levels = paste0("MR", 1:13)))
efa13_loadings_dom <- efa13_loadings %>%
group_by(scale, question) %>%
top_n(1, abs(loading)) %>%
ungroup() %>%
arrange(factor, desc(abs(loading)))
efa13_loadings_dom %>%
count(factor, scale) %>%
complete(factor, scale, fill = list(n = ".")) %>%
spread(scale, n) %>%
kable() %>%
kable_styling()
| factor |
abs |
dse |
exsen |
hthk |
mm |
por |
se |
unev |
wob |
| MR1 |
. |
13 |
. |
. |
. |
3 |
11 |
. |
. |
| MR2 |
32 |
. |
. |
. |
. |
. |
. |
. |
. |
| MR3 |
2 |
. |
. |
. |
. |
. |
. |
. |
6 |
| MR4 |
. |
. |
. |
. |
14 |
3 |
. |
. |
. |
| MR5 |
. |
. |
. |
. |
. |
. |
11 |
. |
. |
| MR6 |
. |
. |
. |
. |
. |
. |
. |
. |
6 |
| MR7 |
. |
1 |
. |
9 |
. |
. |
. |
. |
. |
| MR8 |
. |
. |
. |
. |
. |
. |
. |
6 |
. |
| MR9 |
. |
. |
. |
9 |
. |
. |
. |
. |
. |
| MR10 |
. |
. |
. |
. |
3 |
. |
. |
. |
. |
| MR11 |
. |
. |
. |
. |
. |
10 |
. |
. |
. |
| MR12 |
. |
. |
7 |
. |
. |
. |
. |
. |
. |
| MR13 |
. |
. |
. |
. |
. |
. |
. |
. |
. |
Quick interpretations:
- MR1 = spiritual experience I (dse/se)
- MR2 = absorption
- MR3 = other personality (wob/abs)
- MR4 = martha mary
- MR5 = spiritual experience II (se)
- MR6 = sense of control (wob)
- MR7 = need for cognition I (hthk)
- MR8 = launay-slade (unev)
- MR9 = need for cognition II (hthk)
- MR10 = other porosity
- MR11 = porosity
- MR12 = sheep-goat (exsen)
- [MR13 = NULL]
Focus on items that from DSE, SE, Porosity, & MM scales:
efa13_loadings_dom %>%
mutate(dom = "bold") %>%
select(-loading) %>%
full_join(efa13_loadings) %>%
full_join(efa13_loadings_dom %>%
arrange(desc(factor), abs(loading)) %>%
mutate(order = 1:nrow(.)) %>%
select(scale, question, order)) %>%
mutate(dom = ifelse(is.na(dom), "plain", dom)) %>%
filter(scale %in% c("dse", "se", "por", "mm")) %>%
ggplot(aes(x = factor, y = reorder(question, order),
fill = loading, label = format(round(loading, 2), nsmall =))) +
facet_grid(scale ~ ., scales = "free", space = "free") +
geom_tile(color = "black") +
geom_text(aes(size = dom, fontface = dom)) +
scale_fill_distiller("Factor loading", palette = "RdYlBu", limits = c(-1, 1),
guide = guide_colorbar(barheight = 20, barwidth = 1)) +
scale_x_discrete(position = "top") +
scale_size_manual("Dominant factor?",
values = c(3, 2), labels = c("dominant", "not dominant")) +
theme_minimal() +
theme(axis.title = element_blank())
Joining, by = c("question", "factor", "scale")
Joining, by = c("question", "scale")

Minimizing BIC
VSS(d1)
Very Simple Structure
Call: vss(x = x, n = n, rotate = rotate, diagonal = diagonal, fm = fm,
n.obs = n.obs, plot = plot, title = title, use = use, cor = cor)
VSS complexity 1 achieves a maximimum of 0.72 with 1 factors
VSS complexity 2 achieves a maximimum of 0.8 with 3 factors
The Velicer MAP achieves a minimum of 0 with 8 factors
BIC achieves a minimum of -42163.96 with 6 factors
Sample Size adjusted BIC achieves a minimum of -12097.56 with 8 factors
Statistics by number of factors

Minimizing BIC suggests retaining 6 factors.
efa6 <- fa(d1, 6, rotate = "varimax")
heatmap_fun(efa6, factor_names = paste0("MR", 1:6))
the condition has length > 1 and only the first element will be usedJoining, by = "capacity"
Joining, by = "factor"
Joining, by = "factor"

efa6_loadings <- efa6$loadings[] %>%
data.frame() %>%
rownames_to_column("question") %>%
gather(factor, loading, -question) %>%
mutate(scale = gsub("p7_", "", question),
scale = gsub("_.*$", "", scale),
factor = factor(factor, levels = paste0("MR", 1:6)))
efa6_loadings_dom <- efa6_loadings %>%
group_by(scale, question) %>%
top_n(1, abs(loading)) %>%
ungroup() %>%
arrange(factor, desc(abs(loading)))
efa6_loadings_dom %>%
count(factor, scale) %>%
complete(factor, scale, fill = list(n = ".")) %>%
spread(scale, n) %>%
kable() %>%
kable_styling()
| factor |
abs |
dse |
exsen |
hthk |
mm |
por |
se |
unev |
wob |
| MR1 |
. |
14 |
. |
. |
. |
1 |
10 |
. |
. |
| MR2 |
2 |
. |
. |
6 |
. |
. |
. |
. |
6 |
| MR3 |
32 |
. |
4 |
4 |
. |
. |
. |
1 |
. |
| MR4 |
. |
. |
3 |
. |
17 |
15 |
. |
. |
. |
| MR5 |
. |
. |
. |
. |
. |
. |
12 |
5 |
. |
| MR6 |
. |
. |
. |
8 |
. |
. |
. |
. |
6 |
Quick interpretations:
- MR1 = spiritual experience I (dse/se)
- MR2 = other personality (hthk/wob/abs)
- MR3 = absorption (plus some exsen/hthk/unev)
- MR4 = porosity (mm/por)
- MR5 = spiritual experience II (se/unev)
- MR6 = control (hthk/wob)
Focus on items that from DSE, SE, Porosity, & MM scales:
efa6_loadings_dom %>%
mutate(dom = "bold") %>%
select(-loading) %>%
full_join(efa6_loadings) %>%
full_join(efa6_loadings_dom %>%
arrange(desc(factor), abs(loading)) %>%
mutate(order = 1:nrow(.)) %>%
select(scale, question, order)) %>%
mutate(dom = ifelse(is.na(dom), "plain", dom)) %>%
filter(scale %in% c("dse", "se", "por", "mm")) %>%
ggplot(aes(x = factor, y = reorder(question, order),
fill = loading, label = format(round(loading, 2), nsmall =))) +
facet_grid(scale ~ ., scales = "free", space = "free") +
geom_tile(color = "black") +
geom_text(aes(size = dom, fontface = dom)) +
scale_fill_distiller("Factor loading", palette = "RdYlBu", limits = c(-1, 1),
guide = guide_colorbar(barheight = 20, barwidth = 1)) +
scale_x_discrete(position = "top") +
scale_size_manual("Dominant factor?",
values = c(3, 2), labels = c("dominant", "not dominant")) +
theme_minimal() +
theme(axis.title = element_blank())
Joining, by = c("question", "factor", "scale")
Joining, by = c("question", "scale")

efa6_loadings_dom %>%
mutate(dom = "bold") %>%
select(-loading) %>%
full_join(efa6_loadings) %>%
full_join(efa6_loadings_dom %>%
arrange(desc(factor), abs(loading)) %>%
mutate(order = 1:nrow(.)) %>%
select(scale, question, order)) %>%
mutate(dom = ifelse(is.na(dom), "plain", dom)) %>%
# filter(scale %in% c("dse", "se", "por", "mm")) %>%
ggplot(aes(x = factor, y = reorder(question, order),
fill = loading, label = format(round(loading, 2), nsmall =))) +
facet_grid(scale ~ ., scales = "free", space = "free") +
geom_tile(color = "black") +
geom_text(aes(size = dom, fontface = dom)) +
scale_fill_distiller("Factor loading", palette = "RdYlBu", limits = c(-1, 1),
guide = guide_colorbar(barheight = 20, barwidth = 1)) +
scale_x_discrete(position = "top") +
scale_size_manual("Dominant factor?",
values = c(3, 2), labels = c("dominant", "not dominant")) +
theme_minimal() +
theme(axis.title = element_blank())
Joining, by = c("question", "factor", "scale")
Joining, by = c("question", "scale")

Weisman et al. (2017) retention criteria
reten_fun(d1, "none")
[1] 2
The retention criteria employed in Weisman et al. (2017) suggest retaining 2 factors.
efa2 <- fa(d1, 2, rotate = "varimax")
heatmap_fun(efa2, factor_names = paste0("MR", 1:2))
the condition has length > 1 and only the first element will be usedJoining, by = "capacity"
Joining, by = "factor"
Joining, by = "factor"

efa2_loadings <- efa2$loadings[] %>%
data.frame() %>%
rownames_to_column("question") %>%
gather(factor, loading, -question) %>%
mutate(scale = gsub("p7_", "", question),
scale = gsub("_.*$", "", scale),
factor = factor(factor, levels = paste0("MR", 1:2)))
efa2_loadings_dom <- efa2_loadings %>%
group_by(scale, question) %>%
top_n(1, abs(loading)) %>%
ungroup() %>%
arrange(factor, desc(abs(loading)))
efa2_loadings_dom %>%
count(factor, scale) %>%
complete(factor, scale, fill = list(n = ".")) %>%
spread(scale, n) %>%
kable() %>%
kable_styling()
| factor |
abs |
dse |
exsen |
hthk |
mm |
por |
se |
unev |
wob |
| MR1 |
2 |
13 |
4 |
8 |
17 |
16 |
19 |
. |
5 |
| MR2 |
32 |
1 |
3 |
10 |
. |
. |
3 |
6 |
7 |
Quick interpretations:
- MR1 = porosity + spiritual experience? (“spiritual”?)
- MR2 = absorption + personality? (“secular”?)
Focus on items that from DSE, SE, Porosity, & MM scales:
efa2_loadings_dom %>%
mutate(dom = "bold") %>%
select(-loading) %>%
full_join(efa2_loadings) %>%
full_join(efa2_loadings_dom %>%
arrange(desc(factor), abs(loading)) %>%
mutate(order = 1:nrow(.)) %>%
select(scale, question, order)) %>%
mutate(dom = ifelse(is.na(dom), "plain", dom)) %>%
filter(scale %in% c("dse", "se", "por", "mm")) %>%
ggplot(aes(x = factor, y = reorder(question, order),
fill = loading, label = format(round(loading, 2), nsmall =))) +
facet_grid(scale ~ ., scales = "free", space = "free") +
geom_tile(color = "black") +
geom_text(aes(size = dom, fontface = dom)) +
scale_fill_distiller("Factor loading", palette = "RdYlBu", limits = c(-1, 1),
guide = guide_colorbar(barheight = 20, barwidth = 1)) +
scale_x_discrete(position = "top") +
scale_size_manual("Dominant factor?",
values = c(3, 2), labels = c("dominant", "not dominant")) +
theme_minimal() +
theme(axis.title = element_blank())
Joining, by = c("question", "factor", "scale")
Joining, by = c("question", "scale")

Four-factor solution
Just for the hell of it, here’s a four-factor solution.
efa4 <- fa(d1, 4, rotate = "varimax")
heatmap_fun(efa4, factor_names = paste0("MR", 1:4))
the condition has length > 1 and only the first element will be usedJoining, by = "capacity"
Joining, by = "factor"
Joining, by = "factor"

efa4_loadings <- efa4$loadings[] %>%
data.frame() %>%
rownames_to_column("question") %>%
gather(factor, loading, -question) %>%
mutate(scale = gsub("p7_", "", question),
scale = gsub("_.*$", "", scale),
factor = factor(factor, levels = paste0("MR", 1:4)))
efa4_loadings_dom <- efa4_loadings %>%
group_by(scale, question) %>%
top_n(1, abs(loading)) %>%
ungroup() %>%
arrange(factor, desc(abs(loading)))
efa4_loadings_dom %>%
count(factor, scale) %>%
complete(factor, scale, fill = list(n = ".")) %>%
spread(scale, n) %>%
kable() %>%
kable_styling()
| factor |
abs |
dse |
exsen |
hthk |
mm |
por |
se |
unev |
wob |
| MR1 |
. |
14 |
. |
1 |
. |
. |
19 |
. |
. |
| MR2 |
33 |
. |
6 |
4 |
. |
. |
3 |
6 |
. |
| MR3 |
1 |
. |
. |
7 |
. |
. |
. |
. |
9 |
| MR4 |
. |
. |
1 |
6 |
17 |
16 |
. |
. |
3 |
Quick interpretations:
- MR1 = spiritual experience
- MR2 = absorption (+ secular experience?)
- MR3 = controls
- MR4 = porosity (+ personality?)
Focus on items that from DSE, SE, Porosity, & MM scales:
efa4_loadings_dom %>%
mutate(dom = "bold") %>%
select(-loading) %>%
full_join(efa4_loadings) %>%
full_join(efa4_loadings_dom %>%
arrange(desc(factor), abs(loading)) %>%
mutate(order = 1:nrow(.)) %>%
select(scale, question, order)) %>%
mutate(dom = ifelse(is.na(dom), "plain", dom)) %>%
filter(scale %in% c("dse", "se", "por", "mm")) %>%
ggplot(aes(x = factor, y = reorder(question, order),
fill = loading, label = format(round(loading, 3), nsmall =))) +
facet_grid(scale ~ ., scales = "free", space = "free") +
geom_tile(color = "black") +
geom_text(aes(size = dom, fontface = dom)) +
scale_fill_distiller("Factor loading", palette = "RdYlBu", limits = c(-1, 1),
guide = guide_colorbar(barheight = 30, barwidth = 1)) +
scale_x_discrete(position = "top") +
scale_size_manual("Dominant factor?",
values = c(3, 2), labels = c("dominant", "not dominant")) +
theme_minimal() +
theme(axis.title = element_blank())
Joining, by = c("question", "factor", "scale")
Joining, by = c("question", "scale")

# EXTRA
# hierarchical clustering
clust <- d1 %>% t() %>% dist() %>% hclust()
clust %>%
ggdendrogram(rotate = T) +
theme_minimal() +
theme(panel.grid.major.y = element_blank(),
panel.grid.minor.y = element_blank()) +
labs(title = "Hierarchical agglomerative clustering",
subtitle = "Complete linkage (default for stats::hclust() function)",
y = "Height", x = "Question")

LS0tCnRpdGxlOiAiUGFja2V0IDcgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIgpkYXRlOiAiMjAxOS0wNS0xOSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgpgYGB7ciBnbG9iYWxfb3B0aW9ucywgaW5jbHVkZSA9IEZ9CmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGggPSA0LCBmaWcuYXNwID0gMC42NywKICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUgPSBGLCBlY2hvID0gRikKYGBgCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobGFuZ2NvZykKbGlicmFyeShwc3ljaCkKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShsbWU0KQpsaWJyYXJ5KGxtZXJUZXN0KQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KGdnZGVuZHJvKQoKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCmBgYAoKYGBge3J9CmhlYXRtYXBfZnVuIDwtIGZ1bmN0aW9uKGVmYSwgZmFjdG9yX25hbWVzID0gTkEpewogIAogICMgZ2V0IGZhY3RvciBuYW1lcwogIGlmKGlzLm5hKGZhY3Rvcl9uYW1lcykpewogICAgZmFjdG9yX25hbWVzIDwtIHBhc3RlKCJGYWN0b3IiLCAxOmVmYSRmYWN0b3JzKQogIH0KICAKICAjIHB1dCBmYWN0b3JzIGluIGEgc3RhbmRhcmQgb3JkZXIgd2hlbiBhcHBsaWNhYmxlCiAgYm9keV9mYWN0b3JzIDwtIGZhY3Rvcl9uYW1lc1tncmVwbCgiQk9EWSIsIGZhY3Rvcl9uYW1lcyldCiAgCiAgbGVmdG92ZXJzIDwtIGZhY3Rvcl9uYW1lc1shZmFjdG9yX25hbWVzICVpbiUgYm9keV9mYWN0b3JzXQogIGhlYXJ0X2ZhY3RvcnMgPC0gbGVmdG92ZXJzW2dyZXBsKCJIRUFSVCIsIGxlZnRvdmVycyldCiAgCiAgbGVmdG92ZXJzIDwtIGxlZnRvdmVyc1shbGVmdG92ZXJzICVpbiUgaGVhcnRfZmFjdG9yc10KICBtaW5kX2ZhY3RvcnMgPC0gbGVmdG92ZXJzW2dyZXBsKCJNSU5EIiwgbGVmdG92ZXJzKV0KICAKICBvdGhlcl9mYWN0b3JzIDwtIGxlZnRvdmVyc1shbGVmdG92ZXJzICVpbiUgbWluZF9mYWN0b3JzXQogIAogIGZhY3Rvcl9sZXZlbHMgPC0gYyhib2R5X2ZhY3RvcnMsIGhlYXJ0X2ZhY3RvcnMsIG1pbmRfZmFjdG9ycywgb3RoZXJfZmFjdG9ycykKICAKICAjIGdldCBmYWN0b3IgbG9hZGluZ3MKICBsb2FkaW5ncyA8LSBlZmEkbG9hZGluZ3NbXSAlPiUKICAgIGRhdGEuZnJhbWUoKSAlPiUKICAgIHJvd25hbWVzX3RvX2NvbHVtbigiY2FwYWNpdHkiKSAlPiUKICAgIGdhdGhlcihmYWN0b3IsIGxvYWRpbmcsIC1jYXBhY2l0eSkgJT4lCiAgICBtdXRhdGUoZmFjdG9yID0gYXMuY2hhcmFjdGVyKGZhY3RvcihmYWN0b3IsIGxhYmVscyA9IGZhY3Rvcl9uYW1lcykpLAogICAgICAgICAgIGZhY3RvciA9IGZhY3RvcihmYWN0b3IsIGxldmVscyA9IGZhY3Rvcl9sZXZlbHMpKQogIAogICMgZ2V0IGZhLnNvcnQoKSBvcmRlcgogIG9yZGVyIDwtIGxvYWRpbmdzICU+JQogICAgZ3JvdXBfYnkoY2FwYWNpdHkpICU+JQogICAgdG9wX24oMSwgYWJzKGxvYWRpbmcpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIGFycmFuZ2UoZGVzYyhmYWN0b3IpLCBhYnMobG9hZGluZykpICU+JQogICAgbXV0YXRlKG9yZGVyID0gMTpsZW5ndGgobGV2ZWxzKGZhY3Rvcihsb2FkaW5ncyRjYXBhY2l0eSkpKSkgJT4lCiAgICBzZWxlY3QoY2FwYWNpdHksIG9yZGVyKQogIAogICMgZ2V0IHBlcmNlbnQgc2hhcmVkIHZhcmlhbmNlIGV4cGxhaW5lZAogIHNoYXJlZF92YXIgPC0gZWZhJFZhY2NvdW50ZWQgJT4lCiAgICBkYXRhLmZyYW1lKCkgJT4lCiAgICByb3duYW1lc190b19jb2x1bW4oInN0YXQiKSAlPiUKICAgIGZpbHRlcihzdGF0ID09ICJQcm9wb3J0aW9uIEV4cGxhaW5lZCIpICU+JQogICAgc2VsZWN0KC1zdGF0KSAlPiUKICAgIGdhdGhlcihmYWN0b3IsIHZhcikgJT4lCiAgICBtdXRhdGUoZmFjdG9yID0gYXMuY2hhcmFjdGVyKGZhY3RvcihmYWN0b3IsIGxhYmVscyA9IGZhY3Rvcl9uYW1lcykpLAogICAgICAgICAgIGZhY3RvciA9IGZhY3RvcihmYWN0b3IsIGxldmVscyA9IGZhY3Rvcl9sZXZlbHMpKSAlPiUKICAgIG11dGF0ZSh2YXJfc2hhcmVkID0gcGFzdGUwKGZhY3RvciwgIlxuIiwgcm91bmQodmFyLCAyKSoxMDAsICIlIHNoYXJlZCB2YXIuLCIpKQogIAogICMgZ2V0IHBlcmNlbnQgdG90YWwgdmFyaWFuY2UgZXhwbGFpbmVkCiAgdG90YWxfdmFyIDwtIGVmYSRWYWNjb3VudGVkICU+JQogICAgZGF0YS5mcmFtZSgpICU+JQogICAgcm93bmFtZXNfdG9fY29sdW1uKCJzdGF0IikgJT4lCiAgICBmaWx0ZXIoc3RhdCA9PSAiUHJvcG9ydGlvbiBWYXIiKSAlPiUKICAgIHNlbGVjdCgtc3RhdCkgJT4lCiAgICBnYXRoZXIoZmFjdG9yLCB2YXIpICU+JQogICAgbXV0YXRlKGZhY3RvciA9IGFzLmNoYXJhY3RlcihmYWN0b3IoZmFjdG9yLCBsYWJlbHMgPSBmYWN0b3JfbmFtZXMpKSwKICAgICAgICAgICBmYWN0b3IgPSBmYWN0b3IoZmFjdG9yLCBsZXZlbHMgPSBmYWN0b3JfbGV2ZWxzKSkgJT4lCiAgICBtdXRhdGUodmFyX3RvdGFsID0gcGFzdGUwKHJvdW5kKHZhciwgMikqMTAwLCAiJSB0b3RhbCB2YXIuIikpCiAgCiAgIyBtYWtlIHBsb3QKICBwbG90IDwtIGdncGxvdChsb2FkaW5ncyAlPiUgCiAgICAgICAgICAgICAgICAgICBsZWZ0X2pvaW4ob3JkZXIpICU+JQogICAgICAgICAgICAgICAgICAgbGVmdF9qb2luKHNoYXJlZF92YXIgJT4lIHNlbGVjdCgtdmFyKSkgJT4lCiAgICAgICAgICAgICAgICAgICBsZWZ0X2pvaW4odG90YWxfdmFyICU+JSBzZWxlY3QoLXZhcikpICU+JQogICAgICAgICAgICAgICAgICAgbXV0YXRlKGNhcGFjaXR5ID0gZ3N1YigiXyIsICIgIiwgY2FwYWNpdHkpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGZhY3RvciA9IGZhY3RvcihmYWN0b3IsIGxldmVscyA9IGZhY3Rvcl9sZXZlbHMpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHhsYWIgPSBwYXN0ZSh2YXJfc2hhcmVkLCB2YXJfdG90YWwsIHNlcCA9ICJcbiIpKSwKICAgICAgICAgICAgICAgICBhZXMoeCA9IHJlb3JkZXIoeGxhYiwgYXMubnVtZXJpYyhmYWN0b3IpKSwgCiAgICAgICAgICAgICAgICAgICAgIHkgPSByZW9yZGVyKGNhcGFjaXR5LCBvcmRlciksIAogICAgICAgICAgICAgICAgICAgICBmaWxsID0gbG9hZGluZywgCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKGxvYWRpbmcsIDIpLCBuc21hbGwgPSAyKSkpICsKICAgIGdlb21fdGlsZShjb2xvciA9ICJibGFjayIpICsKICAgIGdlb21fdGV4dChzaXplID0gMykgKwogICAgc2NhbGVfZmlsbF9kaXN0aWxsZXIobGltaXRzID0gYygtMSwgMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJSZFlsQnUiLAogICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9jb2xvcmJhcihiYXJoZWlnaHQgPSAxMCkpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBzY2FsZV94X2Rpc2NyZXRlKHBvc2l0aW9uID0gInRvcCIpICsKICAgIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCiAgCiAgcmV0dXJuKHBsb3QpCiAgCn0KCnNfbW9tZW50cyA8LSBmdW5jdGlvbihwKSB7cCoocCsxKS8yfQpwYXJhbV9lc3QgPC0gZnVuY3Rpb24ocCwgaykge3AqayArIHAgLSAoayooay0xKS8yKX0KY2hlY2tfb2sgPC0gZnVuY3Rpb24ocCwgaykgewogIGEgPC0gKHAtayleMgogIGIgPC0gcCtrCiAgcmV0dXJuKGlmZWxzZShhPmIsIFRSVUUsIEZBTFNFKSkKfQptYXhfb2sgPC0gZnVuY3Rpb24ocCkgewogIGRmX2NoZWNrIDwtIGRhdGEuZnJhbWUoKQogIGZvcihpIGluIDE6cCl7CiAgICBkZl9jaGVja1tpLCJjaGVjayJdIDwtIGNoZWNrX29rKHAsaSkKICB9CiAgbWF4IDwtIGRmX2NoZWNrICU+JSBmaWx0ZXIoY2hlY2spICU+JSBucm93KCkKICByZXR1cm4obWF4KQp9CnJldGVuX2Z1biA8LSBmdW5jdGlvbihkZiwgcm90X3R5cGUgPSBjKCJvYmxpbWluIiwgInZhcmltYXgiLCAibm9uZSIpKXsKICAKICAjIGZpZ3VyZSBvdXQgbWF4IG51bWJlciBvZiBmYWN0b3JzIHRvIHJldGFpbgogIG5fdmFyIDwtIGxlbmd0aChuYW1lcyhkZikpCiAgbWF4X2sgPC0gbWF4X29rKG5fdmFyKQogIAogICMgcnVuIGVmYSB3aXRoIG1heCBmYWN0b3JzLCB1bnJvdGF0ZWQKICBmYV91bnJvdCA8LSBmYShkZiwgbmZhY3RvcnMgPSBtYXhfaywgcm90YXRlID0gIm5vbmUiLCAKICAgICAgICAgICAgICAgICBzY29yZXMgPSAidGVuQmVyZ2UiLCBpbXB1dGUgPSAibWVkaWFuIikKICBlaWdlbiA8LSBmYV91bnJvdCRWYWNjb3VudGVkICU+JQogICAgZGF0YS5mcmFtZSgpICU+JQogICAgcm93bmFtZXNfdG9fY29sdW1uKCJwYXJhbSIpICU+JQogICAgZ2F0aGVyKGZhY3RvciwgdmFsdWUsIC1wYXJhbSkgJT4lCiAgICBzcHJlYWQocGFyYW0sIHZhbHVlKSAlPiUKICAgIGZpbHRlcihgU1MgbG9hZGluZ3NgID4gMSwgYFByb3BvcnRpb24gRXhwbGFpbmVkYCA+IDAuMDUpCiAgcmV0YWluX2sgPC0gbnJvdyhlaWdlbikKICAKICBmYV9yb3QgPC0gZmEoZGYsIG5mYWN0b3JzID0gcmV0YWluX2ssIHJvdGF0ZSA9IHJvdF90eXBlLAogICAgICAgICAgICAgICBzY29yZXMgPSAidGVuQmVyZ2UiLCBpbXB1dGUgPSAibWVkaWFuIikKICAKICBsb2FkaW5ncyA8LSBmYV9yb3QkbG9hZGluZ3NbXSAlPiUKICAgIGRhdGEuZnJhbWUoKSAlPiUKICAgIHJvd25hbWVzX3RvX2NvbHVtbigiY2FwYWNpdHkiKSAlPiUKICAgIGdhdGhlcihmYWN0b3IsIGxvYWRpbmcsIC1jYXBhY2l0eSkgJT4lCiAgICBncm91cF9ieShjYXBhY2l0eSkgJT4lCiAgICB0b3BfbigxLCBhYnMobG9hZGluZykpICU+JQogICAgdW5ncm91cCgpICU+JQogICAgY291bnQoZmFjdG9yKQogIHJldGFpbl9rX2ZpbmFsIDwtIG5yb3cobG9hZGluZ3MpCiAgCiAgcmV0dXJuKHJldGFpbl9rX2ZpbmFsKQp9Cgpzb3VyY2UoIi4vc2NyaXB0cy9wN19kYXRhX3ByZXAuUiIpCmBgYAoKYGBge3J9CmQxIDwtIGQwICU+JQogIHNlbGVjdCgtY29udGFpbnMoIl9kZW1fIiksIC1lbmRzX3dpdGgoIl9jYXQiKSwgLXA3X2N0cnksCiAgICAgICAgIC1jb250YWlucygidG90YWwiKSwgLWNvbnRhaW5zKCJjaGVjayIpKSAlPiUKICBnYXRoZXIocXVlc3Rpb24sIHJlc3BvbnNlLCAtcDdfc3ViaikgJT4lCiAgIyByZXNjYWxlIGV2ZXJ5dGhpbmcgdG8gYmUgaW4gMCwgMQogIG11dGF0ZShzY2FsZSA9IGdzdWIoInA3XyIsICIiLCBxdWVzdGlvbiksCiAgICAgICAgIHNjYWxlID0gZ3N1YigiXy4qJCIsICIiLCBzY2FsZSksCiAgICAgICAgIHJlc3BvbnNlID0gY2FzZV93aGVuKAogICAgICAgICAgIHNjYWxlICVpbiUgYygiYWJzIiwgImV4c2VuIikgfiByZXNwb25zZSwKICAgICAgICAgICBzY2FsZSA9PSAiZHNlIiB+IHJlc3BvbnNlIC8gNSwKICAgICAgICAgICBzY2FsZSA9PSAiaHRoayIgfiAocmVzcG9uc2UgKyAyKSAvIDQsCiAgICAgICAgICAgc2NhbGUgJWluJSBjKCJtbSIsICJ1bmV2IikgfiByZXNwb25zZSAvIDMsCiAgICAgICAgICAgc2NhbGUgPT0gInBvciIgfiByZXNwb25zZSAvIDIsCiAgICAgICAgICAgc2NhbGUgPT0gInNlIiB+IHJlc3BvbnNlIC8gNCwKICAgICAgICAgICBzY2FsZSA9PSAid29iIiB+IChyZXNwb25zZSArIDMpIC8gNikpICU+JQogICMgZ2V0IHJpZCBvZiBmb2xsb3ctdXAgcXVlc3Rpb25zIGZvciBwb3Jvc2l0eQogIGZpbHRlcighZ3JlcGwoIl8uJCIsIHF1ZXN0aW9uKSkgJT4lCiAgc2VsZWN0KC1zY2FsZSkgJT4lCiAgc3ByZWFkKHF1ZXN0aW9uLCByZXNwb25zZSkgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJwN19zdWJqIikKYGBgCgpUaGlzIGlzIGFuIGV4cGxvcmF0aW9uIG9mIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBvbiBQYWNrZXQgNyAobGFzdCB1cGRhdGVkOiAyMDE5LTA1LTE5KS4gS1cgdGhvdWdodCBvZiB0aGlzIGluIHJlc3BvbnNlIHRvIHRoZSBnZW5lcmFsIHF1ZXN0aW9uIG9mIGhvdyAoaWYgYXQgYWxsKSB3ZSBjYW4gZGlzdGluZ3Vpc2ggcG9yb3NpdHkgImJlbGllZnMiIGZyb20gc3Bpcml0dWFsICJleHBlcmllbmNlcy4iIAoKVGhlIGJhc2ljIHF1ZXN0aW9uIGhlcmUgaXMgd2hldGhlciB3ZSBjYW4gZGlzdGluZ3Vpc2ggYmVsaWVmcyBmcm9tIGV4cGVyaWVuY2UgYnkgdHJhY2tpbmcgcGF0dGVybnMgb2YgY292YXJpYW5jZSBhY3Jvc3MgaW5kaXZpZHVhbCBwYXJ0aWNpcGFudHM6IFdoYXQgYXJlIHRoZSAiY2x1c3RlcnMiIG9mIHF1ZXN0aW9ucyB0aGF0IHRlbmRlZCB0byBoYW5nIHRvZ2V0aGVyIGF0IHRoZSBwYXJ0aWNpcGFudCBsZXZlbCAoaS5lLiwgaWYgc29tZW9uZSBlbmRvcnNlZCBvbmUgcXVlc3Rpb24sIHdoYXQgZWxzZSBkaWQgdGhleSBlbmRvcnNlKT8gSWYgcG9yb3NpdHkgYW5kIHNwaXJpdHVhbCBleHBlcmllbmNlIGFyZSBmdWxseSByZWR1bmRhbnQgd2l0aCBlYWNoIG90aGVyLCB3ZSB3b3VsZCBfbm90XyBleHBlY3QgdG8gc2VlIHNlcGFyYXRlIGNsdXN0ZXJzIG9mIHBvcm9zaXR5IHZzLiBzcGlyaXR1YWwgZXhwZXJpZW5jZSBpdGVtcy4KCioqTWFpbiB0YWtlLWF3YXkqKjogSWYgd2UgcmV0YWluIG1vcmUgdGhhbiAyLTMgZmFjdG9ycywgd2UgZW5kIHVwIGRpc3Rpbmd1aXNoaW5nIGJldHdlZW4gcG9yb3NpdHkgYW5kIHNwaXJpdHVhbCBleHBlcmllbmNlLiBUd28gc3RhbmRhcmQgbWV0aG9kcyBvZiBkZXRlcm1pbmluZyBob3cgbWFueSBmYWN0b3JzIHRvIHJldGFpbiAocGFyYWxsZWwgYW5hbHlzaXMgYW5kIG1pbmltaXppbmcgQklDKSBib3RoIHN1Z2dlc3QgcmV0YWluaW5nIHNpeCBvciBtb3JlIGZhY3RvcnMuIAoKCiMgRXhwbG9yYXRvcnkgZmFjdG9yIGFuYWx5c2lzIChFRkEpCgojIyBQYXJhbGxlbCBhbmFseXNpcwoKYGBge3J9CmZhLnBhcmFsbGVsKGQxKQpgYGAKClBhcmFsbGVsIGFuYWx5c2lzIHN1Z2dlc3RzIHJldGFpbmluZyAxMyBmYWN0b3JzLgoKYGBge3J9CmVmYTEzIDwtIGZhKGQxLCAxMywgcm90YXRlID0gInZhcmltYXgiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuYXNwID0gMiwgaW5jbHVkZSA9IFR9CmhlYXRtYXBfZnVuKGVmYTEzLCBmYWN0b3JfbmFtZXMgPSBwYXN0ZTAoIk1SIiwgMToxMykpCmBgYAoKYGBge3J9CmVmYTEzX2xvYWRpbmdzIDwtIGVmYTEzJGxvYWRpbmdzW10gJT4lCiAgZGF0YS5mcmFtZSgpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigicXVlc3Rpb24iKSAlPiUKICBnYXRoZXIoZmFjdG9yLCBsb2FkaW5nLCAtcXVlc3Rpb24pICU+JQogIG11dGF0ZShzY2FsZSA9IGdzdWIoInA3XyIsICIiLCBxdWVzdGlvbiksCiAgICAgICAgIHNjYWxlID0gZ3N1YigiXy4qJCIsICIiLCBzY2FsZSksCiAgICAgICAgIGZhY3RvciA9IGZhY3RvcihmYWN0b3IsIGxldmVscyA9IHBhc3RlMCgiTVIiLCAxOjEzKSkpCmBgYAoKYGBge3J9CmVmYTEzX2xvYWRpbmdzX2RvbSA8LSBlZmExM19sb2FkaW5ncyAlPiUKICBncm91cF9ieShzY2FsZSwgcXVlc3Rpb24pICU+JQogIHRvcF9uKDEsIGFicyhsb2FkaW5nKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoZmFjdG9yLCBkZXNjKGFicyhsb2FkaW5nKSkpCmBgYAoKYGBge3IsIGluY2x1ZGUgPSBUfQplZmExM19sb2FkaW5nc19kb20gJT4lCiAgY291bnQoZmFjdG9yLCBzY2FsZSkgJT4lCiAgY29tcGxldGUoZmFjdG9yLCBzY2FsZSwgZmlsbCA9IGxpc3QobiA9ICIuIikpICU+JQogIHNwcmVhZChzY2FsZSwgbikgJT4lCiAga2FibGUoKSAlPiUKICBrYWJsZV9zdHlsaW5nKCkKYGBgCgpRdWljayBpbnRlcnByZXRhdGlvbnM6CgotICoqTVIxID0gc3Bpcml0dWFsIGV4cGVyaWVuY2UgSSAoZHNlL3NlKSoqCi0gTVIyID0gYWJzb3JwdGlvbgotIE1SMyA9IG90aGVyIHBlcnNvbmFsaXR5ICh3b2IvYWJzKQotICoqTVI0ID0gbWFydGhhIG1hcnkqKgotICoqTVI1ID0gc3Bpcml0dWFsIGV4cGVyaWVuY2UgSUkgKHNlKSoqCi0gTVI2ID0gc2Vuc2Ugb2YgY29udHJvbCAod29iKQotIE1SNyA9IG5lZWQgZm9yIGNvZ25pdGlvbiBJIChodGhrKQotIE1SOCA9IGxhdW5heS1zbGFkZSAodW5ldikKLSBNUjkgPSBuZWVkIGZvciBjb2duaXRpb24gSUkgKGh0aGspCi0gKipNUjEwID0gb3RoZXIgcG9yb3NpdHkqKgotICoqTVIxMSA9IHBvcm9zaXR5KioKLSBNUjEyID0gc2hlZXAtZ29hdCAoZXhzZW4pCi0gW01SMTMgPSBOVUxMXQoKRm9jdXMgb24gKippdGVtcyoqIHRoYXQgZnJvbSBEU0UsIFNFLCBQb3Jvc2l0eSwgJiBNTSBzY2FsZXM6CgpgYGB7ciwgZmlnLndpZHRoID0gNiwgZmlnLmFzcCA9IDAuOSwgaW5jbHVkZSA9IFR9CmVmYTEzX2xvYWRpbmdzX2RvbSAlPiUgCiAgbXV0YXRlKGRvbSA9ICJib2xkIikgJT4lIAogIHNlbGVjdCgtbG9hZGluZykgJT4lCiAgZnVsbF9qb2luKGVmYTEzX2xvYWRpbmdzKSAlPiUKICBmdWxsX2pvaW4oZWZhMTNfbG9hZGluZ3NfZG9tICU+JQogICAgICAgICAgICAgIGFycmFuZ2UoZGVzYyhmYWN0b3IpLCBhYnMobG9hZGluZykpICU+JQogICAgICAgICAgICAgIG11dGF0ZShvcmRlciA9IDE6bnJvdyguKSkgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KHNjYWxlLCBxdWVzdGlvbiwgb3JkZXIpKSAlPiUKICBtdXRhdGUoZG9tID0gaWZlbHNlKGlzLm5hKGRvbSksICJwbGFpbiIsIGRvbSkpICU+JQogIGZpbHRlcihzY2FsZSAlaW4lIGMoImRzZSIsICJzZSIsICJwb3IiLCAibW0iKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yLCB5ID0gcmVvcmRlcihxdWVzdGlvbiwgb3JkZXIpLAogICAgICAgICAgICAgZmlsbCA9IGxvYWRpbmcsIGxhYmVsID0gZm9ybWF0KHJvdW5kKGxvYWRpbmcsIDIpLCBuc21hbGwgPSkpKSArCiAgZmFjZXRfZ3JpZChzY2FsZSB+IC4sIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIpICsKICBnZW9tX3RpbGUoY29sb3IgPSAiYmxhY2siKSArCiAgZ2VvbV90ZXh0KGFlcyhzaXplID0gZG9tLCBmb250ZmFjZSA9IGRvbSkpICsgCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIoIkZhY3RvciBsb2FkaW5nIiwgcGFsZXR0ZSA9ICJSZFlsQnUiLCBsaW1pdHMgPSBjKC0xLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2NvbG9yYmFyKGJhcmhlaWdodCA9IDIwLCBiYXJ3aWR0aCA9IDEpKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShwb3NpdGlvbiA9ICJ0b3AiKSArCiAgc2NhbGVfc2l6ZV9tYW51YWwoIkRvbWluYW50IGZhY3Rvcj8iLCAKICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKDMsIDIpLCBsYWJlbHMgPSBjKCJkb21pbmFudCIsICJub3QgZG9taW5hbnQiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gNiwgZmlnLmFzcCA9IDIsIGluY2x1ZGUgPSBGfQplZmExM19sb2FkaW5nc19kb20gJT4lIAogIG11dGF0ZShkb20gPSAiYm9sZCIpICU+JSAKICBzZWxlY3QoLWxvYWRpbmcpICU+JQogIGZ1bGxfam9pbihlZmExM19sb2FkaW5ncykgJT4lCiAgZnVsbF9qb2luKGVmYTEzX2xvYWRpbmdzX2RvbSAlPiUKICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoZmFjdG9yKSwgYWJzKGxvYWRpbmcpKSAlPiUKICAgICAgICAgICAgICBtdXRhdGUob3JkZXIgPSAxOm5yb3coLikpICU+JQogICAgICAgICAgICAgIHNlbGVjdChzY2FsZSwgcXVlc3Rpb24sIG9yZGVyKSkgJT4lCiAgbXV0YXRlKGRvbSA9IGlmZWxzZShpcy5uYShkb20pLCAicGxhaW4iLCBkb20pKSAlPiUKICAjIGZpbHRlcihzY2FsZSAlaW4lIGMoImRzZSIsICJzZSIsICJwb3IiLCAibW0iKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yLCB5ID0gcmVvcmRlcihxdWVzdGlvbiwgb3JkZXIpLAogICAgICAgICAgICAgZmlsbCA9IGxvYWRpbmcsIGxhYmVsID0gZm9ybWF0KHJvdW5kKGxvYWRpbmcsIDIpLCBuc21hbGwgPSkpKSArCiAgZmFjZXRfZ3JpZChzY2FsZSB+IC4sIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIpICsKICBnZW9tX3RpbGUoY29sb3IgPSAiYmxhY2siKSArCiAgZ2VvbV90ZXh0KGFlcyhzaXplID0gZG9tLCBmb250ZmFjZSA9IGRvbSkpICsgCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIoIkZhY3RvciBsb2FkaW5nIiwgcGFsZXR0ZSA9ICJSZFlsQnUiLCBsaW1pdHMgPSBjKC0xLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2NvbG9yYmFyKGJhcmhlaWdodCA9IDIwLCBiYXJ3aWR0aCA9IDEpKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShwb3NpdGlvbiA9ICJ0b3AiKSArCiAgc2NhbGVfc2l6ZV9tYW51YWwoIkRvbWluYW50IGZhY3Rvcj8iLCAKICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKDMsIDIpLCBsYWJlbHMgPSBjKCJkb21pbmFudCIsICJub3QgZG9taW5hbnQiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgoKIyMgTWluaW1pemluZyBCSUMKCmBgYHtyfQpWU1MoZDEpCmBgYAoKTWluaW1pemluZyBCSUMgc3VnZ2VzdHMgcmV0YWluaW5nIDYgZmFjdG9ycy4KCmBgYHtyfQplZmE2IDwtIGZhKGQxLCA2LCByb3RhdGUgPSAidmFyaW1heCIpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDQsIGZpZy5hc3AgPSAzLCBpbmNsdWRlID0gVH0KaGVhdG1hcF9mdW4oZWZhNiwgZmFjdG9yX25hbWVzID0gcGFzdGUwKCJNUiIsIDE6NikpCmBgYAoKYGBge3J9CmVmYTZfbG9hZGluZ3MgPC0gZWZhNiRsb2FkaW5nc1tdICU+JQogIGRhdGEuZnJhbWUoKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oInF1ZXN0aW9uIikgJT4lCiAgZ2F0aGVyKGZhY3RvciwgbG9hZGluZywgLXF1ZXN0aW9uKSAlPiUKICBtdXRhdGUoc2NhbGUgPSBnc3ViKCJwN18iLCAiIiwgcXVlc3Rpb24pLAogICAgICAgICBzY2FsZSA9IGdzdWIoIl8uKiQiLCAiIiwgc2NhbGUpLAogICAgICAgICBmYWN0b3IgPSBmYWN0b3IoZmFjdG9yLCBsZXZlbHMgPSBwYXN0ZTAoIk1SIiwgMTo2KSkpCmBgYAoKYGBge3J9CmVmYTZfbG9hZGluZ3NfZG9tIDwtIGVmYTZfbG9hZGluZ3MgJT4lCiAgZ3JvdXBfYnkoc2NhbGUsIHF1ZXN0aW9uKSAlPiUKICB0b3BfbigxLCBhYnMobG9hZGluZykpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKGZhY3RvciwgZGVzYyhhYnMobG9hZGluZykpKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gVH0KZWZhNl9sb2FkaW5nc19kb20gJT4lCiAgY291bnQoZmFjdG9yLCBzY2FsZSkgJT4lCiAgY29tcGxldGUoZmFjdG9yLCBzY2FsZSwgZmlsbCA9IGxpc3QobiA9ICIuIikpICU+JQogIHNwcmVhZChzY2FsZSwgbikgJT4lCiAga2FibGUoKSAlPiUKICBrYWJsZV9zdHlsaW5nKCkKYGBgCgpRdWljayBpbnRlcnByZXRhdGlvbnM6CgotICoqTVIxID0gc3Bpcml0dWFsIGV4cGVyaWVuY2UgSSAoZHNlL3NlKSoqCi0gTVIyID0gb3RoZXIgcGVyc29uYWxpdHkgKGh0aGsvd29iL2FicykKLSBNUjMgPSBhYnNvcnB0aW9uIChwbHVzIHNvbWUgZXhzZW4vaHRoay91bmV2KQotICoqTVI0ID0gcG9yb3NpdHkgKG1tL3BvcikqKgotICoqTVI1ID0gc3Bpcml0dWFsIGV4cGVyaWVuY2UgSUkgKHNlL3VuZXYpKioKLSBNUjYgPSBjb250cm9sIChodGhrL3dvYikKCkZvY3VzIG9uICoqaXRlbXMqKiB0aGF0IGZyb20gRFNFLCBTRSwgUG9yb3NpdHksICYgTU0gc2NhbGVzOgoKYGBge3IsIGZpZy53aWR0aCA9IDYsIGZpZy5hc3AgPSAwLjksIGluY2x1ZGUgPSBUfQplZmE2X2xvYWRpbmdzX2RvbSAlPiUgCiAgbXV0YXRlKGRvbSA9ICJib2xkIikgJT4lIAogIHNlbGVjdCgtbG9hZGluZykgJT4lCiAgZnVsbF9qb2luKGVmYTZfbG9hZGluZ3MpICU+JQogIGZ1bGxfam9pbihlZmE2X2xvYWRpbmdzX2RvbSAlPiUKICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoZmFjdG9yKSwgYWJzKGxvYWRpbmcpKSAlPiUKICAgICAgICAgICAgICBtdXRhdGUob3JkZXIgPSAxOm5yb3coLikpICU+JQogICAgICAgICAgICAgIHNlbGVjdChzY2FsZSwgcXVlc3Rpb24sIG9yZGVyKSkgJT4lCiAgbXV0YXRlKGRvbSA9IGlmZWxzZShpcy5uYShkb20pLCAicGxhaW4iLCBkb20pKSAlPiUKICBmaWx0ZXIoc2NhbGUgJWluJSBjKCJkc2UiLCAic2UiLCAicG9yIiwgIm1tIikpICU+JQogIGdncGxvdChhZXMoeCA9IGZhY3RvciwgeSA9IHJlb3JkZXIocXVlc3Rpb24sIG9yZGVyKSwKICAgICAgICAgICAgIGZpbGwgPSBsb2FkaW5nLCBsYWJlbCA9IGZvcm1hdChyb3VuZChsb2FkaW5nLCAyKSwgbnNtYWxsID0pKSkgKwogIGZhY2V0X2dyaWQoc2NhbGUgfiAuLCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWUiKSArCiAgZ2VvbV90aWxlKGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdGV4dChhZXMoc2l6ZSA9IGRvbSwgZm9udGZhY2UgPSBkb20pKSArIAogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKCJGYWN0b3IgbG9hZGluZyIsIHBhbGV0dGUgPSAiUmRZbEJ1IiwgbGltaXRzID0gYygtMSwgMSksCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9jb2xvcmJhcihiYXJoZWlnaHQgPSAyMCwgYmFyd2lkdGggPSAxKSkgKwogIHNjYWxlX3hfZGlzY3JldGUocG9zaXRpb24gPSAidG9wIikgKwogIHNjYWxlX3NpemVfbWFudWFsKCJEb21pbmFudCBmYWN0b3I/IiwgCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygzLCAyKSwgbGFiZWxzID0gYygiZG9taW5hbnQiLCAibm90IGRvbWluYW50IikpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDYsIGZpZy5hc3AgPSAyLCBpbmNsdWRlID0gVH0KZWZhNl9sb2FkaW5nc19kb20gJT4lIAogIG11dGF0ZShkb20gPSAiYm9sZCIpICU+JSAKICBzZWxlY3QoLWxvYWRpbmcpICU+JQogIGZ1bGxfam9pbihlZmE2X2xvYWRpbmdzKSAlPiUKICBmdWxsX2pvaW4oZWZhNl9sb2FkaW5nc19kb20gJT4lCiAgICAgICAgICAgICAgYXJyYW5nZShkZXNjKGZhY3RvciksIGFicyhsb2FkaW5nKSkgJT4lCiAgICAgICAgICAgICAgbXV0YXRlKG9yZGVyID0gMTpucm93KC4pKSAlPiUKICAgICAgICAgICAgICBzZWxlY3Qoc2NhbGUsIHF1ZXN0aW9uLCBvcmRlcikpICU+JQogIG11dGF0ZShkb20gPSBpZmVsc2UoaXMubmEoZG9tKSwgInBsYWluIiwgZG9tKSkgJT4lCiAgIyBmaWx0ZXIoc2NhbGUgJWluJSBjKCJkc2UiLCAic2UiLCAicG9yIiwgIm1tIikpICU+JQogIGdncGxvdChhZXMoeCA9IGZhY3RvciwgeSA9IHJlb3JkZXIocXVlc3Rpb24sIG9yZGVyKSwKICAgICAgICAgICAgIGZpbGwgPSBsb2FkaW5nLCBsYWJlbCA9IGZvcm1hdChyb3VuZChsb2FkaW5nLCAyKSwgbnNtYWxsID0pKSkgKwogIGZhY2V0X2dyaWQoc2NhbGUgfiAuLCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWUiKSArCiAgZ2VvbV90aWxlKGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdGV4dChhZXMoc2l6ZSA9IGRvbSwgZm9udGZhY2UgPSBkb20pKSArIAogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKCJGYWN0b3IgbG9hZGluZyIsIHBhbGV0dGUgPSAiUmRZbEJ1IiwgbGltaXRzID0gYygtMSwgMSksCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9jb2xvcmJhcihiYXJoZWlnaHQgPSAyMCwgYmFyd2lkdGggPSAxKSkgKwogIHNjYWxlX3hfZGlzY3JldGUocG9zaXRpb24gPSAidG9wIikgKwogIHNjYWxlX3NpemVfbWFudWFsKCJEb21pbmFudCBmYWN0b3I/IiwgCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygzLCAyKSwgbGFiZWxzID0gYygiZG9taW5hbnQiLCAibm90IGRvbWluYW50IikpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMgV2Vpc21hbiBldCBhbC4gKDIwMTcpIHJldGVudGlvbiBjcml0ZXJpYQoKYGBge3J9CnJldGVuX2Z1bihkMSwgIm5vbmUiKQpgYGAKClRoZSByZXRlbnRpb24gY3JpdGVyaWEgZW1wbG95ZWQgaW4gV2Vpc21hbiBldCBhbC4gKDIwMTcpIHN1Z2dlc3QgcmV0YWluaW5nIDIgZmFjdG9ycy4KCmBgYHtyfQplZmEyIDwtIGZhKGQxLCAyLCByb3RhdGUgPSAidmFyaW1heCIpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDQsIGZpZy5hc3AgPSAzLCBpbmNsdWRlID0gVH0KaGVhdG1hcF9mdW4oZWZhMiwgZmFjdG9yX25hbWVzID0gcGFzdGUwKCJNUiIsIDE6MikpCmBgYAoKYGBge3J9CmVmYTJfbG9hZGluZ3MgPC0gZWZhMiRsb2FkaW5nc1tdICU+JQogIGRhdGEuZnJhbWUoKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oInF1ZXN0aW9uIikgJT4lCiAgZ2F0aGVyKGZhY3RvciwgbG9hZGluZywgLXF1ZXN0aW9uKSAlPiUKICBtdXRhdGUoc2NhbGUgPSBnc3ViKCJwN18iLCAiIiwgcXVlc3Rpb24pLAogICAgICAgICBzY2FsZSA9IGdzdWIoIl8uKiQiLCAiIiwgc2NhbGUpLAogICAgICAgICBmYWN0b3IgPSBmYWN0b3IoZmFjdG9yLCBsZXZlbHMgPSBwYXN0ZTAoIk1SIiwgMToyKSkpCmBgYAoKYGBge3J9CmVmYTJfbG9hZGluZ3NfZG9tIDwtIGVmYTJfbG9hZGluZ3MgJT4lCiAgZ3JvdXBfYnkoc2NhbGUsIHF1ZXN0aW9uKSAlPiUKICB0b3BfbigxLCBhYnMobG9hZGluZykpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKGZhY3RvciwgZGVzYyhhYnMobG9hZGluZykpKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gVH0KZWZhMl9sb2FkaW5nc19kb20gJT4lCiAgY291bnQoZmFjdG9yLCBzY2FsZSkgJT4lCiAgY29tcGxldGUoZmFjdG9yLCBzY2FsZSwgZmlsbCA9IGxpc3QobiA9ICIuIikpICU+JQogIHNwcmVhZChzY2FsZSwgbikgJT4lCiAga2FibGUoKSAlPiUKICBrYWJsZV9zdHlsaW5nKCkKYGBgCgpRdWljayBpbnRlcnByZXRhdGlvbnM6CgotIE1SMSA9IHBvcm9zaXR5ICsgc3Bpcml0dWFsIGV4cGVyaWVuY2U/ICgic3Bpcml0dWFsIj8pCi0gTVIyID0gYWJzb3JwdGlvbiArIHBlcnNvbmFsaXR5PyAoInNlY3VsYXIiPykKCkZvY3VzIG9uICoqaXRlbXMqKiB0aGF0IGZyb20gRFNFLCBTRSwgUG9yb3NpdHksICYgTU0gc2NhbGVzOgoKYGBge3IsIGZpZy53aWR0aCA9IDMsIGZpZy5hc3AgPSAxLjUsIGluY2x1ZGUgPSBUfQplZmEyX2xvYWRpbmdzX2RvbSAlPiUgCiAgbXV0YXRlKGRvbSA9ICJib2xkIikgJT4lIAogIHNlbGVjdCgtbG9hZGluZykgJT4lCiAgZnVsbF9qb2luKGVmYTJfbG9hZGluZ3MpICU+JQogIGZ1bGxfam9pbihlZmEyX2xvYWRpbmdzX2RvbSAlPiUKICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoZmFjdG9yKSwgYWJzKGxvYWRpbmcpKSAlPiUKICAgICAgICAgICAgICBtdXRhdGUob3JkZXIgPSAxOm5yb3coLikpICU+JQogICAgICAgICAgICAgIHNlbGVjdChzY2FsZSwgcXVlc3Rpb24sIG9yZGVyKSkgJT4lCiAgbXV0YXRlKGRvbSA9IGlmZWxzZShpcy5uYShkb20pLCAicGxhaW4iLCBkb20pKSAlPiUKICBmaWx0ZXIoc2NhbGUgJWluJSBjKCJkc2UiLCAic2UiLCAicG9yIiwgIm1tIikpICU+JQogIGdncGxvdChhZXMoeCA9IGZhY3RvciwgeSA9IHJlb3JkZXIocXVlc3Rpb24sIG9yZGVyKSwKICAgICAgICAgICAgIGZpbGwgPSBsb2FkaW5nLCBsYWJlbCA9IGZvcm1hdChyb3VuZChsb2FkaW5nLCAyKSwgbnNtYWxsID0pKSkgKwogIGZhY2V0X2dyaWQoc2NhbGUgfiAuLCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWUiKSArCiAgZ2VvbV90aWxlKGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdGV4dChhZXMoc2l6ZSA9IGRvbSwgZm9udGZhY2UgPSBkb20pKSArIAogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKCJGYWN0b3IgbG9hZGluZyIsIHBhbGV0dGUgPSAiUmRZbEJ1IiwgbGltaXRzID0gYygtMSwgMSksCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9jb2xvcmJhcihiYXJoZWlnaHQgPSAyMCwgYmFyd2lkdGggPSAxKSkgKwogIHNjYWxlX3hfZGlzY3JldGUocG9zaXRpb24gPSAidG9wIikgKwogIHNjYWxlX3NpemVfbWFudWFsKCJEb21pbmFudCBmYWN0b3I/IiwgCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygzLCAyKSwgbGFiZWxzID0gYygiZG9taW5hbnQiLCAibm90IGRvbWluYW50IikpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDMsIGZpZy5hc3AgPSAzLCBpbmNsdWRlID0gRn0KZWZhMl9sb2FkaW5nc19kb20gJT4lIAogIG11dGF0ZShkb20gPSAiYm9sZCIpICU+JSAKICBzZWxlY3QoLWxvYWRpbmcpICU+JQogIGZ1bGxfam9pbihlZmEyX2xvYWRpbmdzKSAlPiUKICBmdWxsX2pvaW4oZWZhMl9sb2FkaW5nc19kb20gJT4lCiAgICAgICAgICAgICAgYXJyYW5nZShkZXNjKGZhY3RvciksIGFicyhsb2FkaW5nKSkgJT4lCiAgICAgICAgICAgICAgbXV0YXRlKG9yZGVyID0gMTpucm93KC4pKSAlPiUKICAgICAgICAgICAgICBzZWxlY3Qoc2NhbGUsIHF1ZXN0aW9uLCBvcmRlcikpICU+JQogIG11dGF0ZShkb20gPSBpZmVsc2UoaXMubmEoZG9tKSwgInBsYWluIiwgZG9tKSkgJT4lCiAgIyBmaWx0ZXIoc2NhbGUgJWluJSBjKCJkc2UiLCAic2UiLCAicG9yIiwgIm1tIikpICU+JQogIGdncGxvdChhZXMoeCA9IGZhY3RvciwgeSA9IHJlb3JkZXIocXVlc3Rpb24sIG9yZGVyKSwKICAgICAgICAgICAgIGZpbGwgPSBsb2FkaW5nLCBsYWJlbCA9IGZvcm1hdChyb3VuZChsb2FkaW5nLCAyKSwgbnNtYWxsID0pKSkgKwogIGZhY2V0X2dyaWQoc2NhbGUgfiAuLCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWUiKSArCiAgZ2VvbV90aWxlKGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdGV4dChhZXMoc2l6ZSA9IGRvbSwgZm9udGZhY2UgPSBkb20pKSArIAogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKCJGYWN0b3IgbG9hZGluZyIsIHBhbGV0dGUgPSAiUmRZbEJ1IiwgbGltaXRzID0gYygtMSwgMSksCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9jb2xvcmJhcihiYXJoZWlnaHQgPSAyMCwgYmFyd2lkdGggPSAxKSkgKwogIHNjYWxlX3hfZGlzY3JldGUocG9zaXRpb24gPSAidG9wIikgKwogIHNjYWxlX3NpemVfbWFudWFsKCJEb21pbmFudCBmYWN0b3I/IiwgCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygzLCAyKSwgbGFiZWxzID0gYygiZG9taW5hbnQiLCAibm90IGRvbWluYW50IikpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMgRm91ci1mYWN0b3Igc29sdXRpb24KCkp1c3QgZm9yIHRoZSBoZWxsIG9mIGl0LCBoZXJlJ3MgYSBmb3VyLWZhY3RvciBzb2x1dGlvbi4KCmBgYHtyfQplZmE0IDwtIGZhKGQxLCA0LCByb3RhdGUgPSAidmFyaW1heCIpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDQsIGZpZy5hc3AgPSAzLCBpbmNsdWRlID0gVH0KaGVhdG1hcF9mdW4oZWZhNCwgZmFjdG9yX25hbWVzID0gcGFzdGUwKCJNUiIsIDE6NCkpCmBgYAoKYGBge3J9CmVmYTRfbG9hZGluZ3MgPC0gZWZhNCRsb2FkaW5nc1tdICU+JQogIGRhdGEuZnJhbWUoKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oInF1ZXN0aW9uIikgJT4lCiAgZ2F0aGVyKGZhY3RvciwgbG9hZGluZywgLXF1ZXN0aW9uKSAlPiUKICBtdXRhdGUoc2NhbGUgPSBnc3ViKCJwN18iLCAiIiwgcXVlc3Rpb24pLAogICAgICAgICBzY2FsZSA9IGdzdWIoIl8uKiQiLCAiIiwgc2NhbGUpLAogICAgICAgICBmYWN0b3IgPSBmYWN0b3IoZmFjdG9yLCBsZXZlbHMgPSBwYXN0ZTAoIk1SIiwgMTo0KSkpCmBgYAoKYGBge3J9CmVmYTRfbG9hZGluZ3NfZG9tIDwtIGVmYTRfbG9hZGluZ3MgJT4lCiAgZ3JvdXBfYnkoc2NhbGUsIHF1ZXN0aW9uKSAlPiUKICB0b3BfbigxLCBhYnMobG9hZGluZykpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKGZhY3RvciwgZGVzYyhhYnMobG9hZGluZykpKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gVH0KZWZhNF9sb2FkaW5nc19kb20gJT4lCiAgY291bnQoZmFjdG9yLCBzY2FsZSkgJT4lCiAgY29tcGxldGUoZmFjdG9yLCBzY2FsZSwgZmlsbCA9IGxpc3QobiA9ICIuIikpICU+JQogIHNwcmVhZChzY2FsZSwgbikgJT4lCiAga2FibGUoKSAlPiUKICBrYWJsZV9zdHlsaW5nKCkKYGBgCgpRdWljayBpbnRlcnByZXRhdGlvbnM6CgotIE1SMSA9IHNwaXJpdHVhbCBleHBlcmllbmNlCi0gTVIyID0gYWJzb3JwdGlvbiAoKyBzZWN1bGFyIGV4cGVyaWVuY2U/KQotIE1SMyA9IGNvbnRyb2xzCi0gTVI0ID0gcG9yb3NpdHkgKCsgcGVyc29uYWxpdHk/KQoKRm9jdXMgb24gKippdGVtcyoqIHRoYXQgZnJvbSBEU0UsIFNFLCBQb3Jvc2l0eSwgJiBNTSBzY2FsZXM6CgpgYGB7ciwgZmlnLndpZHRoID0gMywgZmlnLmFzcCA9IDEuOCwgaW5jbHVkZSA9IFR9CmVmYTRfbG9hZGluZ3NfZG9tICU+JSAKICBtdXRhdGUoZG9tID0gImJvbGQiKSAlPiUgCiAgc2VsZWN0KC1sb2FkaW5nKSAlPiUKICBmdWxsX2pvaW4oZWZhNF9sb2FkaW5ncykgJT4lCiAgZnVsbF9qb2luKGVmYTRfbG9hZGluZ3NfZG9tICU+JQogICAgICAgICAgICAgIGFycmFuZ2UoZGVzYyhmYWN0b3IpLCBhYnMobG9hZGluZykpICU+JQogICAgICAgICAgICAgIG11dGF0ZShvcmRlciA9IDE6bnJvdyguKSkgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KHNjYWxlLCBxdWVzdGlvbiwgb3JkZXIpKSAlPiUKICBtdXRhdGUoZG9tID0gaWZlbHNlKGlzLm5hKGRvbSksICJwbGFpbiIsIGRvbSkpICU+JQogIGZpbHRlcihzY2FsZSAlaW4lIGMoImRzZSIsICJzZSIsICJwb3IiLCAibW0iKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yLCB5ID0gcmVvcmRlcihxdWVzdGlvbiwgb3JkZXIpLAogICAgICAgICAgICAgZmlsbCA9IGxvYWRpbmcsIGxhYmVsID0gZm9ybWF0KHJvdW5kKGxvYWRpbmcsIDMpLCBuc21hbGwgPSkpKSArCiAgZmFjZXRfZ3JpZChzY2FsZSB+IC4sIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIpICsKICBnZW9tX3RpbGUoY29sb3IgPSAiYmxhY2siKSArCiAgZ2VvbV90ZXh0KGFlcyhzaXplID0gZG9tLCBmb250ZmFjZSA9IGRvbSkpICsgCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIoIkZhY3RvciBsb2FkaW5nIiwgcGFsZXR0ZSA9ICJSZFlsQnUiLCBsaW1pdHMgPSBjKC0xLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2NvbG9yYmFyKGJhcmhlaWdodCA9IDMwLCBiYXJ3aWR0aCA9IDEpKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShwb3NpdGlvbiA9ICJ0b3AiKSArCiAgc2NhbGVfc2l6ZV9tYW51YWwoIkRvbWluYW50IGZhY3Rvcj8iLCAKICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKDMsIDIpLCBsYWJlbHMgPSBjKCJkb21pbmFudCIsICJub3QgZG9taW5hbnQiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gMywgZmlnLmFzcCA9IDMuNywgaW5jbHVkZSA9IEZ9CmVmYTRfbG9hZGluZ3NfZG9tICU+JSAKICBtdXRhdGUoZG9tID0gImJvbGQiKSAlPiUgCiAgc2VsZWN0KC1sb2FkaW5nKSAlPiUKICBmdWxsX2pvaW4oZWZhNF9sb2FkaW5ncykgJT4lCiAgZnVsbF9qb2luKGVmYTRfbG9hZGluZ3NfZG9tICU+JQogICAgICAgICAgICAgIGFycmFuZ2UoZGVzYyhmYWN0b3IpLCBhYnMobG9hZGluZykpICU+JQogICAgICAgICAgICAgIG11dGF0ZShvcmRlciA9IDE6bnJvdyguKSkgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KHNjYWxlLCBxdWVzdGlvbiwgb3JkZXIpKSAlPiUKICBtdXRhdGUoZG9tID0gaWZlbHNlKGlzLm5hKGRvbSksICJwbGFpbiIsIGRvbSkpICU+JQogICMgZmlsdGVyKHNjYWxlICVpbiUgYygiZHNlIiwgInNlIiwgInBvciIsICJtbSIpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBmYWN0b3IsIHkgPSByZW9yZGVyKHF1ZXN0aW9uLCBvcmRlciksCiAgICAgICAgICAgICBmaWxsID0gbG9hZGluZywgbGFiZWwgPSBmb3JtYXQocm91bmQobG9hZGluZywgMyksIG5zbWFsbCA9KSkpICsKICBmYWNldF9ncmlkKHNjYWxlIH4gLiwgc2NhbGVzID0gImZyZWUiLCBzcGFjZSA9ICJmcmVlIikgKwogIGdlb21fdGlsZShjb2xvciA9ICJibGFjayIpICsKICBnZW9tX3RleHQoYWVzKHNpemUgPSBkb20sIGZvbnRmYWNlID0gZG9tKSkgKyAKICBzY2FsZV9maWxsX2Rpc3RpbGxlcigiRmFjdG9yIGxvYWRpbmciLCBwYWxldHRlID0gIlJkWWxCdSIsIGxpbWl0cyA9IGMoLTEsIDEpLAogICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfY29sb3JiYXIoYmFyaGVpZ2h0ID0gMzAsIGJhcndpZHRoID0gMSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKHBvc2l0aW9uID0gInRvcCIpICsKICBzY2FsZV9zaXplX21hbnVhbCgiRG9taW5hbnQgZmFjdG9yPyIsIAogICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoMywgMiksIGxhYmVscyA9IGMoImRvbWluYW50IiwgIm5vdCBkb21pbmFudCIpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCgoKYGBge3IsIGZpZy53aWR0aCA9IDMsIGZpZy5hc3AgPSA0fQojIEVYVFJBCiMgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcKY2x1c3QgPC0gZDEgJT4lIHQoKSAlPiUgZGlzdCgpICU+JSBoY2x1c3QoKQoKY2x1c3QgJT4lCiAgZ2dkZW5kcm9ncmFtKHJvdGF0ZSA9IFQpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHRpdGxlID0gIkhpZXJhcmNoaWNhbCBhZ2dsb21lcmF0aXZlIGNsdXN0ZXJpbmciLCAKICAgICAgIHN1YnRpdGxlID0gIkNvbXBsZXRlIGxpbmthZ2UgKGRlZmF1bHQgZm9yIHN0YXRzOjpoY2x1c3QoKSBmdW5jdGlvbikiLAogICAgICAgeSA9ICJIZWlnaHQiLCB4ID0gIlF1ZXN0aW9uIikKYGBgCg==